import { onMounted, onUnmounted, ref } from "vue";

const tryCatch = async <F extends () => Promise<any>>(
  fn: F
): Promise<[any | null, Awaited<ReturnType<F>> | null]> => {
  try {
    const res = await fn();
    return [null, res] as const;
  } catch (e) {
    return [e as any, null] as const;
  }
};

const wait = (time = 100) =>
  new Promise((resolve) => setTimeout(resolve, time));

function ab2hex(buffer: any[]) {
  const hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ("00" + bit.toString(16)).slice(-2);
    }
  );
  return hexArr.join("");
}

enum Step {
  // 初始化蓝牙模块
  Init,
  // 初始化蓝牙失败
  InitFailed,
  // 开始搜索
  StartDiscovery,
  // 开始搜索失败
  StartDiscoveryFailed,
  // 开始搜索成功
  StartDiscoverySuccess,
  // 获取设备
  GetDevices,
  // 获取设备失败
  GetDevicesFailed,
  // 获取设备成功
  GetDevicesSuccess,
  // 连接设备
  Connect,
  // 设备已连接
  Connected,
  // 连接失败
  ConnectFailed,
}

const useStorage = <T>(key: string) => {
  const value = ref<T | undefined>(undefined);
  onMounted(() => {
    value.value = uni.getStorageSync(key);
  });
  const setValue = (val: T) => {
    try {
      uni.setStorageSync(key, val);
      value.value = val;
    } catch {
      console.error("Failed to set storage:", key, val);
    }
  };
  return [value, setValue] as const;
};

const useBluetooth = () => {
  const step = ref<Step>(Step.Init);
  const devices = ref<UniApp.BluetoothDeviceInfo[]>([]); // 扫描到的设备列表
  const errorMsg = ref<string>("");
  const [connectDeviceId, setConnectDeviceId] =
    useStorage<string>("connectDeviceId");

  // 需要去 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
  const getBluetoothDevices = async () => {
    const [error, result] = await tryCatch(() => uni.getBluetoothDevices());
    if (error || !result) {
      step.value = Step.GetDevicesFailed;
      errorMsg.value = `获取蓝牙设备失败: ${error.message}`;
      return;
    }
    step.value = Step.GetDevicesSuccess;
    devices.value = result.devices || [];
    uni.onBluetoothDeviceFound((res) => {
      res.devices.forEach((device) => {
        if (devices.value.find((d) => d.deviceId === device.deviceId)) {
          // 设备已存在
          return;
        }
        devices.value.push(device);
      });
    });
  };

  // 开启蓝牙设备搜索
  const startBluetoothDevicesDiscovery = async () => {
    step.value = Step.StartDiscovery;
    const [error, result] = await tryCatch(async () =>
      uni.startBluetoothDevicesDiscovery()
    );
    if (error || !result || !result.isDiscovering) {
      step.value = Step.StartDiscoveryFailed;
      errorMsg.value = `开始搜索蓝牙设备失败: ${error.message}`;
      return;
    }
    step.value = Step.StartDiscoverySuccess;
    await wait();
    await getBluetoothDevices();
  };

  // 初始化蓝牙
  const openBluetoothAdapter = async () => {
    const [error] = await tryCatch(() => uni.openBluetoothAdapter());
    if (error) {
      step.value = Step.InitFailed;
      errorMsg.value = `初始化蓝牙失败: ${error.message}`;
      return;
    }
    onInit();
  };

  // 初始化蓝牙 + 获取蓝牙设备
  const onInit = async () => {
    step.value = Step.Init;

    // 获取本机蓝牙适配器状态
    const [error, result] = await tryCatch(() =>
      uni.getBluetoothAdapterState()
    );

    // 获取本机蓝牙适配器状态失败的情况
    if (error || !result) {
      openBluetoothAdapter();
      return;
    }

    const { adapterState } = result as unknown as {
      adapterState: UniNamespace.GetBluetoothAdapterStateSuccess;
    };

    if (!adapterState) {
      // 蓝牙适配器不可用
      openBluetoothAdapter();
      return;
    }

    if (!adapterState!.available) {
      // 蓝牙适配器不可用
      openBluetoothAdapter();
      return;
    }

    if (adapterState!.discovering) {
      // 蓝牙适配器正在搜索设备，
      await tryCatch(() => uni.stopBluetoothDevicesDiscovery());
      await startBluetoothDevicesDiscovery();
      return;
    }

    // 蓝牙适配器未在搜索设备， 开启扫描
    startBluetoothDevicesDiscovery();
  };

  // 连接设备
  const onConnectDevice = async (deviceId: string) => {
    await tryCatch(() => uni.stopBluetoothDevicesDiscovery());
    console.log(step.value, "连接设备", deviceId);

    if (step.value === Step.InitFailed) {
      // 需要重新打开适配器
      const [error] = await tryCatch(() => uni.openBluetoothAdapter());
      if (error) {
        step.value = Step.InitFailed;
        errorMsg.value = `初始化蓝牙失败，无法连接`;
        return;
      }
    }

    const [error] = await tryCatch(() => uni.createBLEConnection({ deviceId }));

    if (error) {
      step.value = Step.ConnectFailed;
      errorMsg.value = `连接设备失败: ${error.message}`;
      return;
    }

    step.value = Step.Connected;
    errorMsg.value = "";
    setConnectDeviceId(deviceId);

    uni.onBLEConnectionStateChange((res) => {
      if (!res.connected) {
        step.value = Step.ConnectFailed;
        errorMsg.value = `设备 ${res.deviceId} 连接失败`;
        return;
      }
      step.value = Step.Connected;
      errorMsg.value = "";
    });

    uni.onBLECharacteristicValueChange((res) => {
      const value = ab2hex(res.value);
      console.log(value);
    });
  };

  onMounted(() => {
    onInit();
  });

  onUnmounted(() => {
    uni.stopBluetoothDevicesDiscovery();
    uni.closeBluetoothAdapter();
    if (connectDeviceId.value) {
      uni.closeBLEConnection({
        deviceId: connectDeviceId.value,
      });
    }
  });

  return {
    step,
    devices,
    errorMsg,
    onConnectDevice,
  };
};

export default useBluetooth;
